package org.comanche.snaf;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;

public class NioServerApp {

	private Executor executor;
	
	private Selector registerSelector;
	
	private Selector clientSelector;
	
	private ServerSocketChannel serverChannel;
	
	private Queue<SocketChannel> queue = new ConcurrentLinkedQueue<SocketChannel>();
	
	private AtomicReference<ListernProcess> processQueue = new AtomicReference<ListernProcess>();
	
	public NioServerApp(){
		executor = Executors.newCachedThreadPool();
		
		try {
			registerSelector = Selector.open();
			clientSelector = Selector.open();
			serverChannel = ServerSocketChannel.open();
			serverChannel.configureBlocking(false);
			serverChannel.register(registerSelector, SelectionKey.OP_ACCEPT);
			serverChannel.socket().setReuseAddress(true);
			serverChannel.socket().bind(new InetSocketAddress(8091),50);
			NewRegisterProcess process = new NewRegisterProcess();
			executor.execute(process);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	private class NewRegisterProcess implements Runnable {

		public void run() {
			System.out.println("begin listening...");
			while (true) {
				try {
					int select = registerSelector.select();
					if (select > 0){
						SocketChannel ch = serverChannel.accept();
						System.out.println("new accept");
						queue.add(ch);
						
						if (processQueue.get() == null){
							ListernProcess listenProcs = new ListernProcess();
							if (processQueue.compareAndSet(null, listenProcs)){
								executor.execute(listenProcs);
							}
						}
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}
	
	private class ListernProcess implements Runnable {
		private SelectionKey selectKey;
		public void run() {
			while(true){
				SocketChannel ch;
				if ((ch = queue.poll()) != null){
					try {
						ch.configureBlocking(false);
						selectKey = ch.register(clientSelector, SelectionKey.OP_READ);
					} catch (ClosedChannelException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
					clientSelector.wakeup();
				}
				
				try {
					int s = clientSelector.select(1000);
					
//					if (s == 0){
//						Set<SelectionKey> keys = clientSelector.keys();
//						if (keys != null){
//							for (SelectionKey selectionKey : keys) {
//								SocketChannel ch1 = (SocketChannel)selectionKey.channel();
//								if (ch1.isConnected()) {
//									//System.out.println(ch1 + "is connected");
//								}
//							}
//						}
//					}
					// read the buffer
					if (s > 0){
						System.out.println("OK");
						Set<SelectionKey> Skeys = clientSelector.selectedKeys();
						if(Skeys != null){
							for (SelectionKey s1 : Skeys) {
								if (s1.isValid() && s1.isReadable()){
									int i = s1.interestOps();
									i = i | SelectionKey.OP_WRITE;
									s1.interestOps(i);
									ByteBuffer inBuffer = ByteBuffer.allocate(10);
									int ret = ((SocketChannel)s1.channel()).read(inBuffer);
									if (ret == -1){
										System.out.println("Error!");
									} else {
										String inStr = new String(inBuffer.array(), 0 , ret);
										System.out.println(inStr);
									}
								} 
							}
						}
						
						
					}
					
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}
		
	}
	
	public static void main(String[] args) {
		NioServerApp app = new NioServerApp();
	}
	
}
